package ca.concordia.pga.main;

import java.util.HashSet;
import java.util.Map;

import ca.concordia.pga.models.Concept;
import ca.concordia.pga.models.PlanningGraph;
import ca.concordia.pga.models.Service;
import ca.concordia.pga.utils.LookAhead;

public class CompositionLogic {
	
	private boolean solved = false;
	private long executionTime;
	private HashSet<Service> newALevelServices;
	private HashSet<Concept> newPlevelConcepts;
	private Map<String, Service> serviceMap;
	private PlanningGraph pg;
	
	public CompositionLogic(PlanningGraph pg,  Map<String, Service> serviceMap) {
		super();		
		this.serviceMap = serviceMap;
		this.pg = pg;
	}
	
	public void start() {
		
		//start timer here
		while(!solved) {
			updateGiven();
			LookAhead.lookAhead(serviceMap, (HashSet<Concept>) pg.getGivenConceptSet());
			newPlevelConcepts = new HashSet<Concept>();
			newALevelServices = new HashSet<Service>();
			callAvailableServices();
			pg.addALevel(newALevelServices);
			pg.addPLevel(newPlevelConcepts);
			cleanServicesMap(serviceMap);
			checkSolved();
		}
		
		System.out.println("");
	}
	
	public long getExecutionTime() {
		return executionTime;
	}
	
	private void updateGiven() {
		//put p levels into given
		for(Concept c : pg.getPLevel(pg.getPLevels().size()-1)) {
			pg.getGivenConceptSet().add(c);
		}
	}
	
	
	private void callAvailableServices() {		
		for(Service s : serviceMap.values()) {
			if(pg.getGivenConceptSet().containsAll(s.getInputConceptSet())) {
				expandService(s);
			}
		}
		
	}
	
	private void expandService(Service s) {
		//get the return type set of Concepts
		HashSet<Concept> outputConcepts = (HashSet<Concept>)s.getOutputConceptSet();
		HashSet<Concept> inputConcepts = (HashSet<Concept>)s.getInputConceptSet();
		
		//add to the P Levels
		for(Concept c : outputConcepts) {
			newPlevelConcepts.add(c);
			c.addProducedByService(s);
			if(pg.getGoalSet().contains(c)) {
				c.setGoal(true);
			}
		}
		
		for(Concept c : inputConcepts) {
			c.addUsedByService(s);
		}
		
		newALevelServices.add(s);
	}
	
	private void cleanServicesMap(Map<String, Service> serviceMap) {
		for(Service s : newALevelServices) {
			serviceMap.remove(s);
		}
	}
	
	private void checkSolved() {
		boolean isSolved = true;
		for(Concept c : pg.getGoalSet()) {
			if(!c.isGoal()) {
				isSolved = false;
			}
		}
		
		solved = isSolved;
	}

}
